﻿/*
 * Copyright (C) 2009-2010 Valerio Fuoglio <valerio.fuoglio@gmail.com>.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */


using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Net;
using System.Threading;
using System.Windows.Forms;
using Lastfm.Scrobbling;
using Lastfm.Services;
using LightScrobbler.Properties;

namespace LightScrobbler
{
    internal partial class ViewerEntry : UserControl
    {
        private const string _lastFmBaseUrl = "http://www.last.fm/music";
        private readonly string _albumUrl;
        private readonly string _artistUrl;
        private readonly Entry _entry;
        private readonly string _titleUrl;

        private Thread _coverThread;
        private Viewer _parent;
        private string _threadId;

        public ViewerEntry(Entry entry, bool checkable)
        {
            InitializeComponent();
            _sendCheckBox.Checked = true;

            if (!checkable)
                _sendCheckBox.Visible = false;

            _entry = entry;

            _artistLink.Text = _entry.Artist;
            _albumLink.Text = _entry.Album;
            _titleLink.Text = _entry.Title;

            _artistUrl = _lastFmBaseUrl + "/" + _artistLink.Text;
            _albumUrl = _artistUrl + "/" + _albumLink.Text;
            _titleUrl = _albumUrl + "/" + _titleLink.Text;

            _artistLink.Links.Add(0, _artistLink.Text.Length, _artistUrl);
            _albumLink.Links.Add(0, _albumLink.Text.Length, _albumUrl);
            _titleLink.Links.Add(0, _titleLink.Text.Length, _titleUrl);

            _durationLabel.Text = "Duration : " + _entry.Duration;
            _playedLabel.Text = "Played : " + entry.TimeStarted.ToShortDateString() + " " +
                                entry.TimeStarted.ToShortTimeString();

            ShowCover(new Bitmap(Resources.unknownAlbum_64x64));

            _artistLink.TabStop = false;
            _albumLink.TabStop = false;
            _titleLink.TabStop = false;
            _playedLabel.TabStop = false;
            _durationLabel.TabStop = false;
            _coverPictureBox.TabStop = false;
        }

        public Entry Entry
        {
            get { return _entry; }
        }

        public bool Send
        {
            get { return _sendCheckBox.Checked; }
            set { _sendCheckBox.Checked = value; }
        }

        public void HideCover()
        {
            _coverPictureBox.Visible = false;
        }

        public void StartCoverFetch(Viewer parent, bool fetch)
        {
            _parent = parent;

            if (fetch)
                ThreadStarter();
            else
            {
                BeginInvoke(new HideCoverCallback(HideCover), null);
            }
        }

        private void ThreadStarter()
        {
            _threadId = Guid.NewGuid().ToString();
            _coverThread = new Thread(FetchCover);
            Utils.SemaphoreThreadRequest.WaitOne();
            _coverThread.Start();
        }

        public void ShowCover(Bitmap coverPic)
        {
            Graphics graph = Graphics.FromImage(coverPic);
            Bitmap jCase = Resources.jewelcase_64x64;
            graph.DrawImage(jCase, new Point(0, 0));

            _coverPictureBox.Image = coverPic;
        }

        public void JoinThread()
        {
            if(_coverThread.IsAlive)
                _coverThread.Join();
        }

        private void FetchCover()
        {
            string coverUrl = null; // Cover image url on lastfm server
            string coverPath = null; // Cover image file in temp folder
            string albumId = _entry.Artist + "_+_+_" + _entry.Album;

            _parent.MutexCoversPath.WaitOne();
            if (!_parent.CoversPath.ContainsKey(albumId))
            {
                _parent.MutexCoversPath.ReleaseMutex();

                _parent.MutexLoadingAlbums.WaitOne();
                bool isLoading = _parent.LoadingAlbums.Contains(albumId);

                if (isLoading)
                {
                    _parent.MutexLoadingAlbums.ReleaseMutex();
                    while (isLoading)
                    {
                        // Requested cover is currently loading
                        Thread.Sleep(500);
                        _parent.MutexCoversPath.WaitOne();
                        bool available = _parent.CoversPath.ContainsKey(albumId);
                        if (available)
                        {
                            coverPath = _parent.CoversPath[albumId];
                            _parent.MutexCoversPath.ReleaseMutex();
                            break;
                        }
                        _parent.MutexCoversPath.ReleaseMutex();
                    }
                }
                else
                {
                    _parent.LoadingAlbums.Add(albumId);
                    _parent.MutexLoadingAlbums.ReleaseMutex();

                    Session session = ScrobbleSessionData.GetInstance().NotAuthenticatedSession;
                    Album album = null;

                    if (session == null)
                    {
                        coverUrl = null;
                    }
                    else
                    {
                        bool albumSuccess = false;

                        try
                        {
                            album = new Album(_entry.Artist, _entry.Album, session);
                            albumSuccess = true;
                        }
                        catch (Exception)
                        {
                            ScrobbleSessionData.GetInstance().InvalidateNotAuthenticatedSession();
                            session = ScrobbleSessionData.GetInstance().NotAuthenticatedSession;

                            try
                            {
                                album = new Album(_entry.Artist, _entry.Album, session);
                            }
                            catch (Exception)
                            {
                                albumSuccess = false;
                            }
                        }

                        if (albumSuccess)
                        {
                            try
                            {
                                coverUrl = album.GetImageURL(AlbumImageSize.Medium);
                            }
                            catch (Exception)
                            {
                                // No network here :'(
                            }
                        }
                    }

                    if (coverUrl != null)
                    {
                        var wc = new WebClient();
                        coverPath = Path.GetTempFileName();

                        try
                        {
                            wc.DownloadFile(coverUrl, coverPath);
                        }
                        catch (Exception)
                        {
                            // We must store a null reference in case download or get url fail
                            // This prevent deadlocks
                            _parent.MutexCoversPath.WaitOne();
                            _parent.CoversPath.Add(albumId, null);
                            _parent.MutexCoversPath.ReleaseMutex();
                            Utils.SemaphoreThreadRequest.Release();
                            return;
                        }

                        _parent.MutexCoversPath.WaitOne();
                        _parent.CoversPath.Add(albumId, coverPath);
                        _parent.MutexCoversPath.ReleaseMutex();
                    }
                    else
                    {
                        // We must store a null reference in case download or get url fail
                        // This prevent deadlocks
                        _parent.MutexCoversPath.WaitOne();
                        _parent.CoversPath.Add(albumId, null);
                        _parent.MutexCoversPath.ReleaseMutex();
                        Utils.SemaphoreThreadRequest.Release();
                        return;
                    }
                }
            }
            else
            {
                coverPath = _parent.CoversPath[albumId];
                _parent.MutexCoversPath.ReleaseMutex();
            }

            if (coverPath == null)
            {
                Utils.SemaphoreThreadRequest.Release();
                return;
            }


            var cover = new Bitmap(coverPath);

            BeginInvoke(new SetAlbumCoverCallback(ShowCover), new object[] {cover});

            Utils.SemaphoreThreadRequest.Release();
        }


        private void _artistLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            _artistLink.Links[_artistLink.Links.IndexOf(e.Link)].Visited = true;
            Process.Start(e.Link.LinkData.ToString());
        }

        private void _albumLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            _albumLink.Links[_albumLink.Links.IndexOf(e.Link)].Visited = true;
            Process.Start(e.Link.LinkData.ToString());
        }

        private void _titleLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            _titleLink.Links[_titleLink.Links.IndexOf(e.Link)].Visited = true;
            Process.Start(e.Link.LinkData.ToString());
        }

        #region Nested type: HideCoverCallback

        internal delegate void HideCoverCallback();

        #endregion

        #region Nested type: SetAlbumCoverCallback

        internal delegate void SetAlbumCoverCallback(Bitmap coverPic);

        #endregion

        #region Nested type: ThreadStartDelagate

        internal delegate void ThreadStartDelagate();

        #endregion
    }
}